package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author xiaohai
 * @since 2024-09-18
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;

    /**
     * 根据赛季查询积分排行榜
     *
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryPointsBoardBySeason(PointsBoardQuery query) {


        // 1.判断查询当前赛季的还是历史赛季的
        Long seasonId = query.getSeason();
        boolean isCurrent = seasonId == null || seasonId == 0;
        // 2.查询当前用户的榜单(积分,排名)
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        PointsBoard myPointsBoard = isCurrent ? queryMyCurrentBoard(key) : queryHistoryBoard(query);
        System.out.println(myPointsBoard);
        // 3.查询排行榜
        List<PointsBoard> pointsBoardList = isCurrent ? queryCurrentBoardList(key, query.getPageNo(), query.getPageSize()) : queryHistoryBoardList(query);
        System.out.println(pointsBoardList);
        // 4.封装PointsBoardVO
        PointsBoardVO vo = new PointsBoardVO();
        // 4.1封装我的积分和排行
        if (myPointsBoard != null) {
            vo.setRank(myPointsBoard.getRank());
            vo.setPoints(myPointsBoard.getPoints());
        }
        // 4.2封装boardList
        if (CollUtils.isEmpty(pointsBoardList)) {
            return vo;
        }
        // 4.3根据用户id查询用户信息
        Set<Long> userIds = pointsBoardList.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        Map<Long, UserDTO> userMap = new HashMap<>();
        if (CollUtils.isNotEmpty(userIds)) {
            List<UserDTO> userList = userClient.queryUserByIds(userIds);
            if (CollUtils.isNotEmpty(userList)) {
                userMap = userList.stream().collect(Collectors.toMap(UserDTO::getId, u -> u));
            }
        }

        // 4.4封装成List<PointsBoardItemVO>,每遍历一次就封装成一个PointsBoardItemVO对象
        List<PointsBoardItemVO> itemList = new ArrayList<>();
        for (PointsBoard pointsBoard : pointsBoardList) {
            PointsBoardItemVO item = new PointsBoardItemVO();
            // 封装积分,排名
            item.setRank(pointsBoard.getRank());
            item.setPoints(pointsBoard.getPoints());
            // 封装用户信息
            UserDTO user = userMap.get(pointsBoard.getUserId());
            if (user != null) {
                item.setName(user.getName());
            }
            itemList.add(item);
        }
        vo.setBoardList(itemList);
        return vo;
    }

    /**
     * 创建表
     *
     * @param tableName
     */
    @Override
    public void createPointsBoardTableBySeason(String tableName) {
        getBaseMapper().createPointsBoardTableBySeason(tableName);
    }

    /**
     * 查询历史赛季的积分排行榜
     *
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoardList(PointsBoardQuery query) {
        return null;
    }

    /**
     * 查询当前赛季的积分排行榜
     *
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
        // 1.计算start stop
        int start = (pageNo - 1) * pageSize;
        int end = start + pageSize - 1;
        // 2.从Redis查询
        Set<ZSetOperations.TypedTuple<String>> tupleSet = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if (CollUtils.isEmpty(tupleSet)) {
            return CollUtils.emptyList();
        }
        // 3.封装List<PointsBoard>
        List<PointsBoard> pointsBoardList = new ArrayList<>();
        int rank = start + 1;
        for (ZSetOperations.TypedTuple<String> tuple : tupleSet) {
            // 每遍历一次就封装成一个PointsBoard对象
            PointsBoard pointsBoard = new PointsBoard();
            String userId = tuple.getValue();
            Double points = tuple.getScore();
            if (userId == null || points == null) {
                continue;
            }
            // 封装用户,积分,排名
            pointsBoard.setUserId(Long.valueOf(userId));            /*封装用户id*/
            pointsBoard.setPoints(points.intValue());               /*封装积分*/
            pointsBoard.setRank(rank);                              /*封装排名*/

            rank++;                                                 /*排名需要累加*/
            pointsBoardList.add(pointsBoard);
        }
        return pointsBoardList;
    }

    /**
     * 查询当前用户历史赛季的积分排行榜
     *
     * @param query
     * @return
     */
    private PointsBoard queryHistoryBoard(PointsBoardQuery query) {

        return null;
    }

    /**
     * 查询当前用户当前赛季的积分排行榜
     *
     * @param key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        Long userId = UserContext.getUser();
        // 1.查询我的排名
        Long rank = redisTemplate.opsForZSet().reverseRank(key, userId.toString());
        // 2.查询我的积分
        Double score = redisTemplate.opsForZSet().score(key, userId.toString());

        PointsBoard myBoard = new PointsBoard();
        myBoard.setRank(rank != null ? rank.intValue() + 1 : 0);
        myBoard.setPoints(score != null ? score.intValue() : 0);
        return myBoard;
    }
}
